package org.deltaset.samples.mvc.meta;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.inject.Inject;
import org.deltaset.faces.PageHelper;
import org.deltaset.faces.component.SelectOption;
import org.deltaset.meta.Catalog;
import org.deltaset.meta.CollectionHandler;
import org.deltaset.meta.EntityResolver;
import org.deltaset.meta.Finder;
import org.deltaset.meta.LabelResolver;
import org.deltaset.meta.MetaAction;
import org.deltaset.meta.MetaActionExecutor;
import org.deltaset.meta.MetaActionHandler;
import org.deltaset.meta.MetaEntry;
import org.deltaset.meta.MetaHandlerFactory;
import org.deltaset.meta.MetaHandlerFactoryBuilder;
import org.deltaset.meta.OptionBuilder;
import org.deltaset.meta.extension.RepositoryProvider;
import org.springframework.stereotype.Service;

/**
 *
 * @author dmakariev
 */
@Service
public class MetaCrudService {

    private static final Logger logger = Logger.getLogger(MetaCrudService.class.getName());
    private final RepositoryProvider repositoryProvider;
    private LazyInit lazyInit;

    @Inject
    public MetaCrudService(final RepositoryProvider repositoryProvider) {
        this.repositoryProvider = repositoryProvider;
    }

    public MetaEntry getMetaEntry(String entityClassName) {
        try {
            final Class entityClass = Class.forName(entityClassName);
            return getMetaEntry(entityClass);
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(MetaCrudService.class.getName()).log(Level.SEVERE, null, ex);
            throw new RuntimeException("not found :" + entityClassName, ex);
        }
    }

    public PageHelper<Object> all(final Finder finder, final String pageNum, final String pageSize) {

        final boolean isPageNumEmpty = null == pageNum || "".equals(pageNum);
        final String currentPageNum = (true == isPageNumEmpty) ? "1" : pageNum;
        //final Finder allFinder = allFinder(metaEntry);
       // final String pageSize = "20";
        final PageHelper<Object> all = fromFinder(finder).load(currentPageNum,pageSize);

        return all;
    }
    
    public Object findById(Class entityClass, String recordId){
        final boolean isEntity = this.isEntity(entityClass);
        if(false==isEntity){
            throw new RuntimeException(" not recognized entityClass:"+entityClass.getName());
        }
          final MetaEntry metaEntry = this.getMetaEntry(entityClass);
        final MetaAction findByIdAction = metaEntry.getFindByIdAction();
        final MetaActionHandler actionHandler = this.getMetaActionHandler();
        final Object recordValue = actionHandler.loadModel(entityClass, findByIdAction, recordId);
        return recordValue;
    }

    private Finder allFinder(final MetaEntry metaEntry) {
        final MetaAction defaultFinder = metaEntry.getDefaultFinder();
        final Finder finder = this.getMetaActionHandler().newPageHelper(null, defaultFinder);
        return finder;
    }

    private MetaEntry getMetaEntry(Class entityClass) {
        final MetaHandlerFactory factory = getLazyInit().getMetaHandlerFactory();
        final MetaEntry metaEntry = factory.createMetaEntry(entityClass);
        return metaEntry;
    }

    public boolean isDisabled(final MetaAction metaAction, final Object model) {
        return getMetaActionHandler().isDisabled(metaAction, model);
    }

    public boolean isHidden(final MetaAction metaAction, final Object model) {
        return getMetaActionHandler().isHidden(metaAction, model);
    }

    private static PageHelper fromFinder(final Finder finder) {
        final PageHelper ph = PageHelper.newInstance(new PageHelper.Finder<Object>() {

            @Override
            public long counter(Object... params) {
                return finder.counter(params);
            }

            @Override
            public List<Object> finder(int firstResult, int maxResult, Object... params) {
                return finder.finder(firstResult, maxResult, params);
            }
        });
        return ph;
    }
    
    public MetaActionExecutor getExecutor(final MetaAction metaAction) {
        final MetaActionExecutor executor = getLazyInit().getMetaHandlerFactory().createMetaActionExecutor(metaAction);
        return executor;
    }
    
    public boolean isEntity(Class checkClass){
        return getLazyInit().getEntityResolver().isEntity(checkClass);
    }

//////////////
    
    public OptionBuilder<SelectOption> getOptionBuilder() {
        return getLazyInit().getOptionBuilder();
    }
    
    EntityResolver getEntityResolver() {
        return getLazyInit().getEntityResolver();
    }

    public CollectionHandler getCollectionHandler() {
        return getLazyInit().getCollectionHandler();
    }

    public MetaActionHandler getMetaActionHandler() {
        return getLazyInit().getMetaActionHandler();
    }

    private LabelResolver getLabelResolver() {
        return getLazyInit().getLabelResolver();
    }
    
    

    private LazyInit getLazyInit() {
        if (null == lazyInit) {
            lazyInit = new LazyInit(repositoryProvider);
        }
        return lazyInit;
    }    

    private static class LazyInit {

        private static final String DEFAULT_TEMPLATE_CATALOG_NAME = "META-INF/deltaset/dsTemplateCatalog.properties";
        private static final String DEFAULT_USUPPORTED_TEMPLATE = "/META-INF/resources/deltaset.meta/internal/fields/unsupported.xhtml";
        private final MetaHandlerFactory metaHandlerFactory;
        private final LabelResolver labelResolver;
        private final Catalog catalog;
        private final EntityResolver entityResolver;
        private final CollectionHandler collectionHandler;
        private final MetaActionHandler metaActionHandler;
        private final OptionBuilder<SelectOption> optionBuilder;
        private final GenericEntityConverter genericEntityConverter;
        // private final MetaCrud metaCrud;

        LazyInit(final RepositoryProvider repositoryProvider) {
            final String catalogName = null;
            final String metaTypeValue = "mvc";
            //final RepositoryProvider repositoryProvider = stateRepository.getValueRepositoryProvider();

            this.metaHandlerFactory = buildMetaHandlerFactory(metaTypeValue, repositoryProvider);
            this.labelResolver = metaHandlerFactory.createLabelResolver();
            this.catalog = metaHandlerFactory.createCatalog(catalogName);
            this.entityResolver = metaHandlerFactory.createEntityResolver();
            this.collectionHandler = metaHandlerFactory.createCollectionHandler();
            this.metaActionHandler = metaHandlerFactory.createMetaActionHandler();
            this.optionBuilder = metaHandlerFactory.createOptionBuilder();
            this.genericEntityConverter = buildGenericEntityConverter(entityResolver);
            // this.metaCrud = new MetaCrud(stateRepository, metaHandlerFactory, metaActionHandler);
        }

        private static MetaHandlerFactory buildMetaHandlerFactory(final String metaTypeValue, final RepositoryProvider repositoryProvider) {
            final MetaHandlerFactoryBuilder builder = MetaHandlerFactoryBuilder.newBuilder();
            builder.catalog(DEFAULT_TEMPLATE_CATALOG_NAME, DEFAULT_USUPPORTED_TEMPLATE, metaTypeValue);
            builder.provider(new CachingMetaHandlerProvider());
            builder.provider(new OptionBuilderProviderImpl());
            builder.provider(repositoryProvider);
            final MetaHandlerFactory metaHandlerFactory = builder.build();
            return metaHandlerFactory;
        }

        private static GenericEntityConverter buildGenericEntityConverter(final EntityResolver entityResolver) {
            final GenericEntityConverter genericEntityConverter = new GenericEntityConverter();
            genericEntityConverter.setEntityResolver(entityResolver);
            return genericEntityConverter;
        }

        public Catalog getCatalog() {
            return catalog;
        }

        public CollectionHandler getCollectionHandler() {
            return collectionHandler;
        }

        public EntityResolver getEntityResolver() {
            return entityResolver;
        }

        public LabelResolver getLabelResolver() {
            return labelResolver;
        }

        public MetaActionHandler getMetaActionHandler() {
            return metaActionHandler;
        }

        public MetaHandlerFactory getMetaHandlerFactory() {
            return metaHandlerFactory;
        }

        public OptionBuilder<SelectOption> getOptionBuilder() {
            return optionBuilder;
        }

        public GenericEntityConverter getGenericEntityConverter() {
            return genericEntityConverter;
        }
//        public MetaCrud getMetaCrud() {
//            return metaCrud;
//        }
    }
}
